using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Castle.DynamicProxy;
using EntityAop.Interceptors;

namespace EntityAop.Configuration
{
    public class EntityAopConfiguration
    {
        public static EntityAopConfiguration Instance { get; private set; }

        static  EntityAopConfiguration()
        {
            Instance = new EntityAopConfiguration();
        }

        private static string locker = "locker";

        protected static Type INotifyPropertyChangedType = typeof (INotifyPropertyChanged);

        public Type TargetCollectionType { get; protected set; }

        public virtual IList<IInterceptorWithRelatedTypes> MessageReceiveInterceptors { get; protected set; }
        public virtual Func<ProxyGenerationOptions> ProxyGenerationOptionsFunc { get; protected set; }

        public virtual Func<ProxyGenerator> ProxyGeneratorCreateFunc { get; protected set; }

        internal bool IsInProxyCreationPhase { get; set;}

        protected EntityAopConfiguration()
        {
            lock(locker)
            {
                Init();
            }
        }

        protected virtual void Init()
        {
            WithTargetCollectionType(typeof(ObservableCollection<>));
            MessageReceiveInterceptors = new List<IInterceptorWithRelatedTypes>();

            ProxyGenerationOptionsFunc = () => ProxyGenerationOptions.Default;

            ProxyGeneratorCreateFunc = () => new ProxyGenerator();
        }

        public virtual EntityAopConfiguration WithTargetCollectionType(Type targetCollectionType)
        {
            if (!INotifyPropertyChangedType.IsAssignableFrom(targetCollectionType))
            {
                throw new ArgumentException("targetCollectionType must be descendant from INotifyPropertyChanged");
            }

            TargetCollectionType = targetCollectionType;

            return this;
        }

        public virtual EntityAopConfiguration WithProxyGeneratorCreateFunc(Func<ProxyGenerator> proxyGeneratorCreateFunc)
        {
            ProxyGeneratorCreateFunc = proxyGeneratorCreateFunc;

            return this;
        }

        public virtual EntityAopConfiguration WithProxyGenerationOptions(Func<ProxyGenerationOptions> proxyGenerationOptionsFunc)
        {
            this.ProxyGenerationOptionsFunc = proxyGenerationOptionsFunc;

            return this;
        }

        public virtual EntityAopConfiguration WithMessageReceiveInterceptor(IInterceptorWithRelatedTypes interceptor)
        {
            var interceptorType = interceptor.GetType();

            if (MessageReceiveInterceptors.Any( existingInterceptor => existingInterceptor.GetType() == interceptorType ))
            {
                return this;
            }

            MessageReceiveInterceptors.Add(interceptor);

            return this;
        }

        public virtual T CreateProxiedInstance<T>(T input)
        {
            using (var proxyGenerator = ProxyGeneratorCreateFunc())
            {
                return proxyGenerator.CreateClassProxyWithTargetRecursive(input, ProxyGenerationOptionsFunc(), MessageReceiveInterceptors.ToArray());
            }
        }

        public virtual object UnproxyInstance<T>(T input)
        {
            using (var proxyGenerator = ProxyGeneratorCreateFunc())
            {
                return proxyGenerator.Unproxy(input);
            }
        }

    }
}
